package com.gpj.learn.netty.buffer.simple;

import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
import java.security.SecureRandom;

/**
 * @className BufferSimpleDemo
 * @description
 *   buffer使用demo
 * @author GPJ
 * @date 2020/2/11 10:03
 * @version 1.0
 **/
@Slf4j
public class BufferSimpleDemo {
    /**
     * buffer 使用demo
     */
    public void bufferDemo(){
        // 创建buffer
        IntBuffer buffer = IntBuffer.allocate(10);
        // buffer初始状态
        BufferLogUtil.bufferInfoLog(buffer);

        // 向buffer中写入信息
        while (buffer.position() < buffer.capacity()){
            buffer.put(new SecureRandom().nextInt(20));
        }
        // buffer写入之后状态
        BufferLogUtil.bufferInfoLog(buffer);

        // buffer翻转(写 --> 读)
        buffer.flip();
        // buffer翻转之后的状态
        BufferLogUtil.bufferInfoLog(buffer);

     // 输出读取buffer中的数据
        BufferLogUtil.bufferDatasLog(buffer);
    }

    /**
     * 通channel向buffer写入数据
     */
    public void channelReadDemo(){
        String classPath = this.getClass().getResource("/").toString().substring(6);
        try (
            FileInputStream in = new FileInputStream(new File(classPath,"readTestFile.txt"));
            final FileChannel channel = in.getChannel();
        ){
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            channel.read(buffer);

            buffer.flip();

            BufferLogUtil.bufferDatasLog(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 通channel从buffer读取数据
     */
    public void channelWriteDemo(){
        String classPath = this.getClass().getResource("/").toString().substring(6);
        try (
            FileOutputStream out = new FileOutputStream(new File(classPath,"writeTestFile.txt"));
            final FileChannel channel = out.getChannel();
        ){
            String data = "通过channel读取buffer中的数据，然后写入文件中";

            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.put(data.getBytes());

            buffer.flip();

            channel.write(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 通channel与buffer复制文件
     */
    public void channelReadWriteDemo(){
        String classPath = this.getClass().getResource("/").toString().substring(6);
        try (
                FileInputStream in = new FileInputStream(new File(classPath,"readTestFile.txt"));
                FileOutputStream out = new FileOutputStream(new File(classPath,"writeTestFile.txt"));
                final FileChannel inChannel = in.getChannel();
                final FileChannel outChannel = out.getChannel();
        ){
            ByteBuffer buffer = ByteBuffer.allocate(4);
            while (true){
                final int readNum = inChannel.read(buffer);
                log.info("readNum --> {}", readNum);
                if(readNum <= 0){
                    break;
                }
                buffer.flip();
                outChannel.write(buffer);
                buffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * byteBuiffer顺序问题
     * bytebuffer是可以写入任意类型数据(布尔除外)，但是写入顺序必须与读取顺序一致，否则读取出的数据为乱码或报错
     */
    public void bufferOrder(){
        // 正确读取顺序
        final ByteBuffer buffer = ByteBuffer.allocate(20);
        buffer.putChar('a');// 字符 = 2字节 = 16位
        buffer.putInt(1);// int = 4字节 = 32位
        buffer.putDouble(1.2);// double = 8字节 = 64位

        buffer.flip();

        log.info("读取buffer中的char --> {}", buffer.getChar());
        log.info("读取buffer中的int --> {}", buffer.getInt());
        log.info("读取buffer中的double --> {}", buffer.getDouble());


        buffer.clear();


        // 错误读取顺序
        buffer.putChar('a');
        buffer.putInt(1);
        buffer.putDouble(1.2);

        log.info("读取buffer中的int --> {}", buffer.getInt());
        log.info("读取buffer中的char --> {}", buffer.getChar());
        log.info("读取buffer中的double --> {}", buffer.getDouble());
    }

    /**
     * slice方法可以基于当前buffer建新buffer，内容与原缓冲区共享
     * 新buffer内容是从源buffer的到limit这段数据，并且数据共享
     */
    public void sliceBuffer(){
        final IntBuffer buffer = BufferLogUtil.generateIntBufferWithDatas(4);

        buffer.flip();

        BufferLogUtil.bufferDatasLog(buffer);

        buffer.position(0);
        // 创建新字节缓冲区，内容与原缓冲区共享
        final IntBuffer sliceBuffer = buffer.slice();
        sliceBuffer.position(2);
        sliceBuffer.put(12);

        BufferLogUtil.bufferDatasLog(buffer);
    }

    /**
     * 只读buffer
     */
    public void readOnlyBuffer(){
        final IntBuffer buffer = BufferLogUtil.generateIntBufferWithDatas(4);
        final IntBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();

        buffer.flip();
        BufferLogUtil.bufferDatasLog(buffer);

        readOnlyBuffer.flip();
        log.info("---------------------- buffer内容输出 start ---------------------------");
        while (readOnlyBuffer.hasRemaining()){
            log.info("{}", readOnlyBuffer.get());
        }
        log.info("---------------------- buffer内容输出 end ---------------------------");

        try {
            readOnlyBuffer.position(2);
            readOnlyBuffer.put(3);
        }catch (Exception e){
            log.error("不可向只读buffer中写数据");
        }
    }

    /**
     * jvm内存外buffer的使用
     */
    public void DirectBuffer(){
        String classPath = this.getClass().getResource("/").toString().substring(6);
        try (
                FileInputStream in = new FileInputStream(new File(classPath,"readTestFile.txt"));
                FileOutputStream out = new FileOutputStream(new File(classPath,"writeTestFile.txt"));
                final FileChannel inChannel = in.getChannel();
                final FileChannel outChannel = out.getChannel();
        ){
            final ByteBuffer buffer = ByteBuffer.allocateDirect(4);

            while(true){
                final int readNum = inChannel.read(buffer);
                if(readNum <= 0){
                    break;
                }

                buffer.flip();
                outChannel.write(buffer);

                buffer.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 基于数组复制buffer，数据共享
     */
    public void wrapBuffer(){
        byte[] bytes = "这是一个测试用bytes数组".getBytes();
        byte[] changeBbytes = "这是一个修改用bytes数组".getBytes();

        final ByteBuffer wrapBuffer = ByteBuffer.wrap(bytes);

        wrapBuffer.flip();
        BufferLogUtil.bufferDatasLog(wrapBuffer);

        for (int i = 0; i < bytes.length; i++) {
            if(bytes[i] != changeBbytes[i]){
                bytes[i] = changeBbytes[i];
            }
        }

        wrapBuffer.flip();
        BufferLogUtil.bufferDatasLog(wrapBuffer);
    }
}
